<?php


namespace Melon;


use EventHttpRequest;

class Request
{
    public const METHOD_HEAD = 'HEAD';
    public const METHOD_GET = 'GET';
    public const METHOD_POST = 'POST';
    public const METHOD_PUT = 'PUT';
    public const METHOD_PATCH = 'PATCH';
    public const METHOD_DELETE = 'DELETE';
    public const METHOD_OPTIONS = 'OPTIONS';
    public const METHOD_TRACE = 'TRACE';
    public const METHOD_CONNECT = 'CONNECT';

    /**
     * @var string
     */
    protected string $pathInfo;

    /**
     * Query
     * @var array
     */
    protected array $query = [];

    /**
     * @var array
     */
    protected array $post = [];

    /**
     * @var array
     */
    protected array $header = [];

    /**
     * @var Application
     */
    protected Application $app;

    /**
     * @var Worker
     */
    protected Worker $worker;

    /**
     * @var EventHttpRequest
     */
    protected EventHttpRequest $request;

    /**
     * Request constructor.
     * @param EventHttpRequest $request
     * @param Worker $worker
     * @param Application $application
     */
    public function __construct(EventHttpRequest $request, Worker $worker, Application $application)
    {
        $this->app = $application;

        $this->worker = $worker;

        $this->request = $request;

        $this->parse();
    }

    protected function parse()
    {
        // parse headers
        $headers = $this->request->getInputHeaders();

        foreach ($headers as $key => $item) {
            $this->header[strtolower($key)] = $item;
        }

        // parse uri
        $uri = $this->request->getUri();
        $uri = parse_url($uri);
        parse_str($uri['query'] ?? '', $this->query);

        // parse path
        $this->pathInfo = $uri['path'];

        // parse post
        if (in_array($this->method(), [self::METHOD_POST])) {
            $buf = $this->request->getInputBuffer();
            $buf = $buf->read($buf->length);

            $content_type = $this->header('content-type', '');

            if ($content_type === 'application/x-www-form-urlencoded') {
                parse_str(urldecode($buf), $this->post);
            } elseif ($content_type === 'application/json') {
                $this->post = json_decode($buf, true);
            } elseif (str_contains($content_type, "/form-data")) {
                // TODO
            }
        }
    }

    public function header(string $key, string $default = null): ?string
    {
        return $this->header[$key] ?? $default;
    }

    /**
     * 获取路径
     * @return string
     */
    public function path(): string
    {
        return $this->pathInfo;
    }

    /**
     * 获取请求方式
     * @return string
     */
    public function method(): string
    {
        $cmd = $this->request->getCommand();

        switch ($cmd) {
            case EventHttpRequest::CMD_GET:
                return self::METHOD_GET;
            case EventHttpRequest::CMD_POST:
                return self::METHOD_POST;
            case EventHttpRequest::CMD_HEAD:
                return self::METHOD_HEAD;
            case EventHttpRequest::CMD_PUT:
                return self::METHOD_PUT;
            case EventHttpRequest::CMD_DELETE:
                return self::METHOD_DELETE;
            case EventHttpRequest::CMD_OPTIONS:
                return self::METHOD_OPTIONS;
            case EventHttpRequest::CMD_TRACE:
                return self::METHOD_TRACE;
            case EventHttpRequest::CMD_CONNECT:
                return self::METHOD_CONNECT;
            case EventHttpRequest::CMD_PATCH:
                return self::METHOD_PATCH;
        }
        return strval($cmd);
    }

    /**
     * 获取Get查询
     * @param $key
     * @param null $default
     * @return mixed
     */
    public function query($key, $default = null)
    {
        return $this->query[$key] ?? $default;
    }

    public function post($key = null, $default = null)
    {
        return $this->post[$key] ?? $default;
    }

    /**
     * @return int
     */
    public function getWorkerId(): int
    {
        return spl_object_id($this->worker);
    }
}
